/*
 * File:				context_menu.c
 * Created:				April 2008
 * Created by:			Axel von Bertoldi
 * Last Modified:		April 2008
 * Last Modified by:	Axel von Bertoldi
 * (C) 2005-2008		Axel von Bertoldi
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the Free
 * Software Foundation; either version 2 of the License, or (at your option)
 * any later version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
 * more details.
 *
 * You should have received a copy of the GNU General Public License along with
 * this program; if not, write to:
 * The Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor
 * Boston, MA 02110-1301, USA.
 */

#include <glade/glade-xml.h>
#include <glib/gprintf.h>

#include "context-menu.h"
#include "vfs.h"
#include "utils.h"
#include "config.h"

static Garbage garbage = NULL;

/******************************************************************************/
static char *archive_mime_types[] = {
	"application/x-ar",
	"application/x-arj",
	"application/x-bzip",
	"application/x-bzip-compressed-tar",
	"application/x-compress",
	"application/x-compressed-tar",
	"application/x-deb",
	"application/x-gtar",
	"application/x-gzip",
	"application/x-lha",
	"application/x-lhz",
	"application/x-rar",
	"application/x-rar-compressed",
	"application/x-tar",
	"application/x-zip",
	"application/x-zip-compressed",
	"application/zip",
	"multipart/x-zip",
	"application/x-rpm",
	"application/x-jar",
	"application/x-java-archive",
	"application/x-lzop",
	"application/x-zoo",
	"application/x-cd-image",
	"application/x-7z-compressed",
	"application/x-gzpostscript",
	"application/x-ms-dos-executable",
	NULL
};
/******************************************************************************/
static void
tree_set_sensitive (GtkWidget *menu_item, gboolean sensitive) {
	if (DEBUG) g_printf ("In %s\n", __FUNCTION__);
	/* walk up the menu browser tree from the menu item that causes the popup
	 * menu and disable all the menu shells, stopping at the menu bar. Need to
	 * do this to get around the focus bug. Would be nicer if we could do this
	 * w/o changing the appearance of the widgets  */
	GtkWidget *current = menu_item->parent;
	while (current) {
		gtk_widget_set_sensitive (current, sensitive);

		if (GTK_IS_MENU_BAR (current)) return;

		current = GTK_MENU_SHELL (current)->parent_menu_shell;
	}
}
/******************************************************************************/
/* Stolen from menu.c from the gnome-panel */
static void
restore_grabs (GtkWidget *w, gpointer data) {
	if (DEBUG) g_printf ("In %s\n", __FUNCTION__);

    GtkWidget *menu_item = data;
    GtkMenu *menu = GTK_MENU(menu_item->parent); 
    GtkWidget *xgrab_shell;
    GtkWidget *parent;

    tree_set_sensitive (menu_item, TRUE);

    /* Find the last viewable ancestor, and make an X grab on it */
	parent = GTK_WIDGET (menu);
	xgrab_shell = NULL;
	while (parent) {
		gboolean viewable = TRUE;
		GtkWidget *tmp = parent;

		while (tmp) {
			if (!GTK_WIDGET_MAPPED (tmp)) {
				viewable = FALSE;
				break;
			}
			tmp = tmp->parent;
		}

		if (viewable)
			xgrab_shell = parent;

		parent = GTK_MENU_SHELL (parent)->parent_menu_shell;
	}

	/*only grab if this HAD a grab before*/
	if (xgrab_shell && (GTK_MENU_SHELL (xgrab_shell)->have_xgrab)) {
	    if (gdk_pointer_grab (xgrab_shell->window, TRUE,
				              GDK_BUTTON_PRESS_MASK |
                              GDK_BUTTON_RELEASE_MASK |
                              GDK_ENTER_NOTIFY_MASK |
                              GDK_LEAVE_NOTIFY_MASK,
                              NULL, NULL, 0) == 0) {

            if (gdk_keyboard_grab (xgrab_shell->window, TRUE, GDK_CURRENT_TIME) == 0)
                GTK_MENU_SHELL (xgrab_shell)->have_xgrab = TRUE;
            else
                gdk_pointer_ungrab (GDK_CURRENT_TIME);

        }
    }
	gtk_grab_add (GTK_WIDGET (menu));
}
/******************************************************************************/
static void
close_menu_browser (GtkWidget *menu) {
	if (DEBUG) g_printf ("In %s\n", __FUNCTION__);

	GtkWidget *browser = g_object_get_data (G_OBJECT (menu), "menu_browser");
	GtkWidget *parent = gtk_widget_get_parent (GTK_WIDGET (browser));
    gtk_menu_shell_deactivate (GTK_MENU_SHELL (parent));
}
/******************************************************************************/
static void
context_menu_clean_up (GtkWidget *menu) {
	if (DEBUG) g_printf ("In %s\n", __FUNCTION__);

    gtk_widget_destroy (menu);
    garbage_empty (&garbage, FALSE);
}
/******************************************************************************/
static void
context_menu_setup_callback (const gchar *app,
                             const gchar* file,
                             GtkMenuItem *menu_item,
                             GtkMenu *menu) {
	if (DEBUG) g_printf ("In %s\n", __FUNCTION__);

	gchar **args = g_strv_new (ARGS_SIZE);
	args[ARG_APP]  = g_strdup (app);
	args[ARG_FILE] = g_strdup (file);

	g_signal_connect_swapped (menu_item,
							  "activate",
							  G_CALLBACK (vfs_launch_application),
							  args);

    g_signal_connect_swapped (menu_item,
                              "activate",
                              G_CALLBACK (close_menu_browser),
                              menu);

	garbage_add_item (garbage, args[ARG_APP]);
	garbage_add_item (garbage, args[ARG_FILE]);
	garbage_add_item (garbage, args);
}
/******************************************************************************/
static void
context_menu_add_new_dir_callback (GtkWidget *menu_item, gchar *file_name) {
	if (DEBUG) g_printf ("In %s\n", __FUNCTION__);

    GtkWidget *menu = gtk_widget_get_parent (menu_item);

    close_menu_browser (menu);

	GError *error = NULL;

	GladeXML* xml = glade_xml_new (GLADEUI_PATH, "new_dir_dialog", NULL);
	g_return_if_fail (xml != NULL);

	GtkWidget *new_dir_dialog = glade_xml_get_widget (xml, "new_dir_dialog");
	GtkWidget *new_dir_entry  = glade_xml_get_widget (xml, "new_dir_entry");

	if (gtk_dialog_run (GTK_DIALOG (new_dir_dialog)) == GTK_RESPONSE_ACCEPT) {
		const gchar *entry_text = gtk_entry_get_text (GTK_ENTRY (new_dir_entry));
		gchar *new_dir = g_strdup_printf ("%s/%s", file_name, entry_text);

		GFile *file = g_file_new_for_path (new_dir);
		g_file_make_directory (file, NULL, &error);
		g_object_unref (file);

		/* open the dir if we succeeded in creating it */
		if (utils_gerror_ok (&error, TRUE)) {
			vfs_file_do_default_action (new_dir);
		}
	}
	gtk_widget_destroy (new_dir_dialog);
	g_free (file_name);
}
/******************************************************************************/
static void
context_menu_add_new_dir (const gchar *file_name, GtkWidget *menu) {
	if (DEBUG) g_printf ("In %s\n", __FUNCTION__);

	if (!vfs_file_is_directory (file_name)) return;

	GtkWidget *menu_item = gtk_image_menu_item_new_with_mnemonic (_("_New Folder Here"));
	gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (menu_item),
								   gtk_image_new_from_stock (GTK_STOCK_NEW,
								   							 GTK_ICON_SIZE_MENU));
	gtk_menu_shell_append (GTK_MENU_SHELL (menu), menu_item);

	g_signal_connect (GTK_MENU_ITEM (menu_item),
                      "activate",
                      G_CALLBACK (context_menu_add_new_dir_callback),
                      g_strdup (file_name));
}
/******************************************************************************/
static void
context_menu_add_burn (const gchar *file_name, GtkWidget *menu) {
	if (DEBUG) g_printf ("In %s\n", __FUNCTION__);

	GtkWidget *menu_item = gtk_image_menu_item_new_with_mnemonic (_("_Create CD/DVD"));
	gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (menu_item),
								   gtk_image_new_from_icon_name ("nautilus-cd-burner",
								   								 GTK_ICON_SIZE_MENU));
	gtk_menu_shell_append (GTK_MENU_SHELL (menu), menu_item);

	context_menu_setup_callback ("brasero",
                                 file_name,
                                 GTK_MENU_ITEM (menu_item),
                                 GTK_MENU (menu));
}
/******************************************************************************/
static void
context_menu_add_compile_tex (const gchar *file_name, GtkWidget *menu) {
	if (DEBUG) g_printf ("In %s\n", __FUNCTION__);

	if (!g_str_has_suffix (file_name, "tex")) return;

	GtkWidget *menu_item = gtk_image_menu_item_new_with_mnemonic (_("_Build Latex Document"));
	gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (menu_item),
								   gtk_image_new_from_icon_name ("build",
								   								 GTK_ICON_SIZE_MENU));
	gtk_menu_shell_append (GTK_MENU_SHELL (menu), menu_item);

	context_menu_setup_callback ("rubber -f --inplace -d",
                                 file_name,
                                 GTK_MENU_ITEM (menu_item),
                                 GTK_MENU (menu));
}
/******************************************************************************/
static gboolean
is_archive (const gchar *file_name) {
	if (DEBUG) g_printf ("In %s\n", __FUNCTION__);

	gboolean ret = FALSE;
	GFile*	   file = g_file_new_for_path (file_name);
	GFileInfo* file_info =  g_file_query_info (file,
											   G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE,
											   0,
											   NULL,											   
											   NULL);

	const gchar* content_type = g_file_info_get_content_type (file_info);

	int i;
	for (i = 0; archive_mime_types[i] != NULL; i++) {
		if (g_strcmp0 (content_type, archive_mime_types[i]) == 0) {
			ret = TRUE;
			break;
		}
	}
	g_object_unref (file_info);
	g_object_unref (file);

	return ret;
}
/******************************************************************************/
static void
context_menu_add_archive_action (const gchar *file_name, GtkWidget *menu) {
	if (DEBUG) g_printf ("In %s\n", __FUNCTION__);

	gchar *archive_label = NULL;
	gchar *archive_action = NULL;

	if (is_archive (file_name)) {
		archive_label = _("_Extract Here");
		archive_action = "file-roller -h";
	}
	else {
		archive_label = _("Create _Archive");
		archive_action = "file-roller -d";
	}

	GtkWidget *menu_item = gtk_image_menu_item_new_with_mnemonic (archive_label);
	gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (menu_item),
								   gtk_image_new_from_icon_name ("package",
								   								 GTK_ICON_SIZE_MENU));
	gtk_menu_shell_append (GTK_MENU_SHELL (menu), menu_item);

	context_menu_setup_callback (archive_action,
                                 file_name,
                                 GTK_MENU_ITEM (menu_item),
                                 GTK_MENU (menu));
}
/******************************************************************************/
static void
context_menu_add_open_with_item (const gchar *file_name, GtkWidget *menu) {
	if (DEBUG) g_printf ("In %s\n", __FUNCTION__);

	GList *root = vfs_get_all_mime_applications (file_name);
	GList *apps = root;
	
	g_return_if_fail (root != NULL);

	GtkWidget *menu_item = gtk_image_menu_item_new_with_mnemonic (_("_Open With"));
	gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (menu_item),
								   gtk_image_new_from_stock (GTK_STOCK_OPEN,
								   							 GTK_ICON_SIZE_MENU));
	gtk_menu_shell_append (GTK_MENU_SHELL (menu), menu_item);

	GtkWidget *sub_menu = gtk_menu_new ();

	gtk_menu_item_set_submenu (GTK_MENU_ITEM (menu_item), sub_menu);

	while (apps != NULL) {
		menu_item = gtk_image_menu_item_new_with_label (g_app_info_get_name (apps->data));
		gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (menu_item),
									   gtk_image_new_from_gicon (g_app_info_get_icon (apps->data),
									   							 GTK_ICON_SIZE_MENU));

		gtk_menu_shell_append (GTK_MENU_SHELL (sub_menu), menu_item);

		context_menu_setup_callback (g_app_info_get_executable (apps->data),
									 file_name,
                                     GTK_MENU_ITEM (menu_item),
                                     GTK_MENU (menu));

		g_object_unref (apps->data);
		apps = apps->next;
	}
	g_list_free (root);
}
/******************************************************************************/
static void
context_menu_add_trash_item (const gchar *file_name, GtkWidget *menu) {
	if (DEBUG) g_printf ("In %s\n", __FUNCTION__);
						  
	GtkWidget *menu_item = gtk_image_menu_item_new_with_mnemonic (_("_Move to Trash"));
	gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (menu_item),
														gtk_image_new_from_stock (GTK_STOCK_DELETE,
																				  GTK_ICON_SIZE_MENU));
	gtk_menu_shell_append (GTK_MENU_SHELL (menu), menu_item);
	
	g_signal_connect_swapped (G_OBJECT (menu_item),
							  "activate",
							  G_CALLBACK (vfs_file_trash),
							  (gpointer) g_strdup (file_name));

    g_signal_connect_swapped (menu_item,
                              "activate",
                              G_CALLBACK (close_menu_browser),
                              menu);
}
/******************************************************************************/
static void
context_menu_populate (const gchar *file_name, GtkWidget *menu) {
	if (DEBUG) g_printf ("In %s\n", __FUNCTION__);

	context_menu_add_open_with_item	(file_name, menu);
	gtk_menu_shell_append (GTK_MENU_SHELL (menu), gtk_separator_menu_item_new());
	context_menu_add_new_dir		(file_name, menu);
	context_menu_add_trash_item		(file_name, menu);
	gtk_menu_shell_append (GTK_MENU_SHELL (menu), gtk_separator_menu_item_new());
	context_menu_add_archive_action	(file_name, menu);
	context_menu_add_compile_tex	(file_name, menu);
	context_menu_add_burn			(file_name, menu);

	gtk_widget_show_all (menu);
}
/******************************************************************************/
gboolean
context_menu_display (const gchar *file_name, GtkWidget *menu_item) {
	if (DEBUG) g_printf ("In %s\n", __FUNCTION__);

	int event_button;
	int event_time;

	GdkEventButton *event = g_object_get_data (G_OBJECT (menu_item), "button_event");
	if (event) {
		event_button = event->button;
		event_time = event->time;
	}
	else {
		event_button = 3;
		event_time = gtk_get_current_event_time();
	}

	garbage_init (&garbage);

	GtkWidget *menu = gtk_menu_new ();

	/* add some data to the popup menu so we can get to it later */
	GtkWidget *menu_browser = g_object_get_data (G_OBJECT (menu_item), "menu_browser");
	g_object_set_data (G_OBJECT (menu), "menu_browser", menu_browser);
	g_object_set_data (G_OBJECT (menu), "menu_item", menu_item);

    g_signal_connect_swapped (menu_item,
                              "destroy",
                              G_CALLBACK (context_menu_clean_up),
                              menu);

	g_signal_connect (GTK_MENU_SHELL (menu),
                      "deactivate",
                      G_CALLBACK (restore_grabs),
                      menu_item);

	context_menu_populate (file_name, menu);

	/* disable the menu browser to get around the focus bug */
    tree_set_sensitive (menu_item, FALSE);

	gtk_menu_popup (GTK_MENU (menu),
					NULL,
					NULL,
					NULL,
					NULL,
					event_button,
					event_time);
	return TRUE;
}
/******************************************************************************/
